#ifndef _WIN32
#include <sys/time.h>
#endif
#include "Server.h"


#include "AdminChannelManager.h"
#include "AdminUserManager.h"
#include "AdminRightManager.h"
#include "Buffer.h"
#include "Host.h"
#include "AdminHost.h"
#include "AdminSerializer.h"

#define READ_LEN 10000

using namespace AgoraServer;

void	Server::ForcedDisconnection(AgoraAdmin::IUser *user, void *data)
{
	Server* serv = reinterpret_cast<Server*>(data);
	serv->_mapIUserClient[user]->GetConnectionSound()->CloseSocket();
	serv->_mapIUserClient[user]->GetConnectionTxtAdm()->CloseSocket();
}

Server::Server()
{
	this->_emp = new Empacter();
	this->_cnt = new AgoraCore::Host();
	this->_cntTxtAdm = new AgoraCore::Host();
	ThreadPool::InitThreadPool(100);
	this->_octIn = 0;
	this->_octOut = 0;
	this->_clientListNbAcces = 0;
	this->_mtxClientListModification = new Mutex();
	this->_mtxClientListBrowse = new Mutex();
	this->_mtxAdminAcces = new Mutex();
	this->_adminServer = new AgoraAdmin::Host();
	this->_interpreter = new AgoraAdmin::AdminInterpreter(AgoraAdmin::ChannelManager::GetChannelManager(), AgoraAdmin::UserManager::GetUserManager());
	this->_interpreter->SubscribeForcedDisconnectionUser(&Server::ForcedDisconnection, this);
}


void	Server::AcceptClientTxtAdm(void* server)
{
	Server*	s = reinterpret_cast<Server*>(server);
	int id = 0;
	Client *clt;
	info_select	*inf;

	while (true)
	{
		AgoraCore::Connection* cnt;
		if (s->_cntTxtAdm->Accept(&cnt) == 0)
		{
			clt = new Client(id++, cnt);
			s->AddClient(clt);
			ThreadPool::Request *rqt = new ThreadPool::Request();
			rqt->function = &Server::SelectReceiveTxtAdm;
			rqt->autoFree = true;
			inf = new Server::info_select();
			inf->client = clt;
			inf->server = s;
			rqt->parameters = inf;
			ThreadPool::GetThreadPool()->AddRequest(rqt);
		}
	}
}

void	Server::AcceptClientSound(void* server)
{
	Server*	s = reinterpret_cast<Server*>(server);
	Client *clt = 0;
	info_selectSound	*inf;

	while (true)
	{
		AgoraCore::Connection* cnt;
		if (s->_cnt->Accept(&cnt) == 0)
		{
			
			ThreadPool::Request *rqt = new ThreadPool::Request();
			rqt->function = &Server::SelectReceiveSound;
			rqt->autoFree = true;
			inf = new Server::info_selectSound();
			inf->server = s;
			inf->connection = cnt;
			rqt->parameters = inf;
			ThreadPool::GetThreadPool()->AddRequest(rqt);
		}
	}
}

bool Server::AuthClient(info_select* inf)
{
	unsigned int	len;
	char			buff[READ_LEN];
	int			id(inf->client->GetId());
	std::string		*login = 0;
	std::string		*pass = 0;
	unsigned int				size;
	unsigned int				toRead(0);
	timeval			tmR;

	size = sizeof(int);
	inf->client->GetConnectionTxtAdm()->SendData(&id, &size);
	//DEBUG
	std::cout << "new client : " << (int)id << std::endl;

	while (true)
	{
		len = READ_LEN;
		tmR.tv_sec = 120;
		tmR.tv_usec = 0;
		if (inf->client->GetConnectionTxtAdm()->ReceiveData(buff, &len, 0) == 0)
		{

			if (len > 0)
			{
				inf->server->_octIn += len;
				inf->client->GetBufferTxtAdm()->Write(buff, len);
				size = inf->client->GetBufferTxtAdm()->Size();
				while ((toRead && size >= toRead) || (!toRead && size >= sizeof(int)))
				{
					if (!toRead && size >= sizeof(int))
					{
						toRead = *((unsigned int*)inf->client->GetBufferTxtAdm()->Read(sizeof(int)));
						size -= sizeof(int);
					}
					else if (toRead && size >= toRead)
					{
						if (!login)
						{
							login = new std::string((char*)inf->client->GetBufferTxtAdm()->Read(toRead));
							//DEBUG
							std::cout << "client " << (int)id << " : login = " << *login << std::endl;
							size -= toRead;
							toRead = 0;
						}
						else
						{
							pass = new std::string((char*)inf->client->GetBufferTxtAdm()->Read(toRead));
							//DEBUG
							std::cout << "client " << (int)id << " : pass = " << *pass << std::endl;
							inf->client->GetBufferTxtAdm()->Clean();
							// CHECK PASSWORD && CHECK ALREADY CONNECTED
							inf->server->_mtxAdminAcces->Lock();
							AgoraAdmin::IUser* user = AgoraAdmin::UserManager::GetUserManager()->GetUser(*login);
							if (user && !(user->IsConnected()) && inf->server->_adminServer->Authentification(*login, *pass))
							{
								//LoadUser(*login);
								user->SetConnected(true);
								AgoraAdmin::ChannelManager::GetChannelManager()->MoveUser(0, 0, user);
								inf->server->_mapIUserClient[user] = inf->client;
								inf->server->_mapClientIUser[inf->client] = user;
								size = 1;
								inf->client->GetConnectionTxtAdm()->SendData((void*)"1",&size,0);
								delete login;
								delete pass; 
								AgoraAdmin::AdminInterpreter::ReturnInfo* ret = inf->server->_interpreter->ConnectUser(user);
								inf->server->SendAdmReturnInfo(inf->client, ret);
								inf->server->_mtxAdminAcces->Unlock();
								return true;
							}
							else
							{
								inf->server->_mtxAdminAcces->Unlock();
								size = 1;
								inf->client->GetConnectionTxtAdm()->SendData((void*)"0",&size,0);
								delete login;
								delete pass;
								return false;
							}
							inf->server->_mtxAdminAcces->Unlock();
							//inf->client->SetUser(*login, *pass);
							// DEBUG
							/*unsigned int sizett = 1;
							inf->client->GetConnectionTxtAdm()->SendData("t",&sizett,0);*/

							/*delete login;
							delete pass;
							return;*/
						}
					}
				}

			}
			else
				break;
		}
		else
			break;
	}
	return false;
}

void Server::SendAdmReturnInfo(Client* client, AgoraAdmin::AdminInterpreter::ReturnInfo* ret)
{
	unsigned int size;

	client->GetBufferCmdAdmData()->Write(ret->_informations, ret->_size);
	delete ret->_informations;
	client->GetInfoCmd().function = ret->_function;
	size = client->GetBufferCmdAdmData()->Size();
	this->GetEmpacter()->EmpactAdmin(client->GetBufferCmdAdmData(), client->GetBufferCmdAdmSend(), size, &(client->GetInfoCmd()));
	std::list<AgoraAdmin::IUser *>::iterator iteCurrent = ret->_userConcerned.begin();
	std::list<AgoraAdmin::IUser *>::iterator iteEnd = ret->_userConcerned.end();
	while (iteCurrent != iteEnd)
	{
		if (this->_mapIUserClient.count((*iteCurrent)))
		  {
			this->_mapIUserClient[(*iteCurrent)]->SendBufferTxtAdm(client->GetBufferCmdAdmSend()->Read(client->GetBufferCmdAdmSend()->Size(), false), client->GetBufferCmdAdmSend()->Size());
		  }
		++iteCurrent;
	}
	//inf->client->SendBufferTxtAdm(inf->client->GetBufferCmdAdmSend()->Read(inf->client->GetBufferCmdAdmSend()->Size(), false), inf->client->GetBufferCmdAdmSend()->Size());
	client->GetBufferCmdAdmSend()->Clean();
	delete ret;
}

void Server::SelectReceiveTxtAdm(void* infoSelect)
{
	// TODO DEBUG
	
	/*AgoraAdmin::AdminInterpreter *interpreter = new AgoraAdmin::AdminInterpreter(AgoraAdmin::ChannelManager::GetChannelManager(), AgoraAdmin::UserManager::GetUserManager());*/
	/*Buffer data(50000);
	Buffer tosend(50000);
	InfoAdmin info;*/

	info_select*	inf = reinterpret_cast<info_select*>(infoSelect);
	unsigned int	len;
	char			buff[50000];
	Header			head;
	void			*packet;
	unsigned int	size;
	timeval			tmR;
	
	if (Server::AuthClient(inf))
	{
		while (true)
		{
			len = READ_LEN;
			tmR.tv_sec = 120;
			tmR.tv_usec = 0;
			if (inf->client->GetConnectionTxtAdm()->ReceiveData(buff, &len, 0) == 0)
			{
				if (len > 0)
				{
					inf->server->_octIn += len;
					inf->client->GetBufferTxtAdm()->Write(buff, len);
					size = inf->client->GetBufferTxtAdm()->Size();
					while ((packet = inf->server->GetEmpacter()->Desempact(inf->client->GetBufferTxtAdm()->Read(inf->client->GetBufferTxtAdm()->Size(), false), size, &head)))
					{
						inf->client->GetBufferTxtAdm()->Read(size);
						if (head.base->type == ADMIN)
						{
							inf->server->_mtxAdminAcces->Lock();
							std::stack<AgoraAdmin::AdminInterpreter::ReturnInfo*> retStack = inf->server->GetAdminInterpreter()->Launch(inf->server->_mapClientIUser[inf->client], head.admin->function, packet);
							while (!retStack.empty())
							{
								AgoraAdmin::AdminInterpreter::ReturnInfo* ret = retStack.top();
								AgoraCore::AdminFunction	func = ret->_function;
								unsigned int info;
								if (func == KICKUSERSERVER || func == BANUSERSERVER || func == UNBANUSERSERVER)
									info = *(unsigned int*)ret->_informations;
								if (func == GETBANUSERLISTSERVER)
								{
									std::map<unsigned int, AgoraAdmin::IUser*>	lst;
									std::list<unsigned int>::const_iterator	iteCur = inf->server->_adminServer->GetBanListUser().begin();
									std::list<unsigned int>::const_iterator	iteEnd = inf->server->_adminServer->GetBanListUser().end();
									while (iteCur != iteEnd)
									{
										AgoraAdmin::IUser* u = AgoraAdmin::UserManager::GetUserManager()->GetUser(*iteCur);
										lst[u->GetId()] = u;
										iteCur++;
									}
									void* data = AgoraAdmin::AdminSerializer::SerializeUserList(lst, ret->_size);
									ret->_informations = new char[ret->_size];
									memcpy(ret->_informations, data, ret->_size);
								}
								
								inf->server->SendAdmReturnInfo(inf->client, ret);
									
								if (func == BANUSERSERVER)
								{
									inf->server->_adminServer->AddBanListUser(info);
								}
								else if (func == UNBANUSERSERVER)
								{
									inf->server->_adminServer->RemoveBanListUser(info);
								}
								if ((func == KICKUSERSERVER || func == BANUSERSERVER) && inf->server->_mapIUserClient.count(AgoraAdmin::UserManager::GetUserManager()->GetUser(info)))
								{
									Client* cl = inf->server->_mapIUserClient[AgoraAdmin::UserManager::GetUserManager()->GetUser(info)];
									if (cl->GetConnectionSound())
										cl->GetConnectionSound()->CloseSocket();
								}
								retStack.pop();
							}
							inf->server->_mtxAdminAcces->Unlock();
							inf->client->GetBufferCmdAdmSend()->Clean();
						}
						size = inf->client->GetBufferTxtAdm()->Size();
					}
				}
				else
					break;
			}
			else
				break;
		}
	}
	inf->server->_mtxClientListModification->Lock();
	if (!inf->server->IsClientConnected(inf->client))
	{
		inf->server->_mtxClientListModification->Unlock();
		return;
	}
	if (inf->client->GetConnectionSound())
		inf->client->GetConnectionSound()->CloseSocket();
	inf->client->GetConnectionTxtAdm()->CloseSocket();
	inf->server->_mtxClientListModification->Unlock();
}



void Server::SelectReceiveSound(void* data)
{
	info_selectSound* inf = reinterpret_cast<info_selectSound*>(data);
	unsigned int	len;
	char			buff[READ_LEN];
	Header			head;
	void			*packet;
	unsigned int				size;
	int				id;
	timeval			tmR;
	Client			*clt = 0;

	tmR.tv_sec = 120;
	tmR.tv_usec = 0;
	// Recuperation du client....
			size = sizeof(int);
			// TODO : controle de sizedp ... le return est violent
			if (inf->connection->ReceiveData(&id, &size, &tmR) || size != sizeof(int))
			{
				return;
			}
			std::list<Client*>::iterator it = inf->server->GetClient()->begin();
			std::list<Client*>::iterator end = inf->server->GetClient()->end();
			for(;it != end; ++it)
			{
				if ((*it)->GetId() == id)
				{
					clt = *it;
					break;
				}
			}
			if (!clt)
			{
				return;
			}
			//DEBUG
			std::cout << "client " << id << " connection sound" << std::endl;
			// TODO controle identite du client
			clt->SetConnectionSound(inf->connection);

// TODO opti			if (clt->GetConnectionTxtAdm() is connected)
	while (true)
	{
		len = READ_LEN;
		tmR.tv_sec = 1;
		tmR.tv_usec = 0;
		if (inf->connection->ReceiveData(buff, &len, &tmR) == 0)
		{
			
			if (len > 0)
			{
				inf->server->_octIn += len;
				clt->GetBufferSound()->Write(buff, len);
				size = clt->GetBufferSound()->Size();
				while (inf->server->GetEmpacter()->Desempact(clt->GetBufferSound()->Read(clt->GetBufferSound()->Size(), false), size, &head))
				{
					packet = clt->GetBufferSound()->Read(size);
					if (head.base->type == AUDIO)
					{
						if (AgoraAdmin::RightManager::GetRightManager()->GetChannelRight(inf->server->_mapClientIUser[clt], AgoraAdmin::ChannelManager::GetChannelManager()->GetChannel(inf->server->_mapClientIUser[clt]->GetCurrentChannelId())).GetCanTalkChannel())
						{
							inf->server->_mtxClientListModification->Lock();
							if (!inf->server->_mtxClientListBrowse->IsLock())
								inf->server->_mtxClientListBrowse->Lock();
							inf->server->_mtxClientListModification->Unlock();
							inf->server->_clientListNbAcces++;
							std::list<AgoraAdmin::IUser*>::iterator it = AgoraAdmin::ChannelManager::GetChannelManager()->GetChannel(inf->server->_mapClientIUser[clt]->GetCurrentChannelId())->GetUser().begin();
							std::list<AgoraAdmin::IUser*>::iterator end = AgoraAdmin::ChannelManager::GetChannelManager()->GetChannel(inf->server->_mapClientIUser[clt]->GetCurrentChannelId())->GetUser().end();
							for(;it != end; ++it)
							{
								if (inf->server->_mapIUserClient.count(*it) && inf->server->_mapIUserClient[*it]->GetId() != clt->GetId())
								{
									if (inf->server->_mapIUserClient[*it]->SendBufferSound(packet, size) == 0)
										inf->server->_octOut += size;
								}
							}
							inf->server->_clientListNbAcces--;
							if (!inf->server->_clientListNbAcces)
								inf->server->_mtxClientListBrowse->Unlock();
						}
					}
					size = clt->GetBufferSound()->Size();
				}
			}
		}
		else
			break;
	}
	inf->server->RemoveClient(clt);
}

unsigned int Server::Start(unsigned short int soundPort, unsigned short int admTxtPort, const std::string& address)
{
	if (address == "")
	{
		if (this->_cnt->Init(soundPort) || this->_cntTxtAdm->Init(admTxtPort))
			return (1);
	}
	else
	{
		if (this->_cnt->Init(soundPort, address) || this->_cntTxtAdm->Init(admTxtPort, address))
			return (2);
	}
	if (this->_cnt->Listen() || this->_cntTxtAdm->Listen())
		return (3);

	ThreadPool::Request *rqt = new ThreadPool::Request();
	rqt->function = &Server::AcceptClientSound;
	rqt->autoFree = true;
	rqt->parameters = this;
	ThreadPool::GetThreadPool()->AddRequest(rqt);
	rqt = new ThreadPool::Request();
	rqt->function = &Server::AcceptClientTxtAdm;
	rqt->autoFree = true;
	rqt->parameters = this;
	ThreadPool::GetThreadPool()->AddRequest(rqt);
	return (0);
}

std::list<Client*>*	Server::GetClient()
{
	return &(this->_clientList);
}

IEmpacter*	Server::GetEmpacter()
{
	return this->_emp;
}

bool		Server::IsClientConnected(Client* client)
{
	std::list<Client*>::iterator it = this->_clientList.begin();
	std::list<Client*>::iterator itend = this->_clientList.end();

	for(; it != itend; ++it)
	{
		if ((*it) == client)
			return true;
	}
	return false;
}

void		Server::RemoveClient(Client* client)
{
	this->_mtxClientListModification->Lock();
	if (!this->IsClientConnected(client))
		return;
	if (client->GetConnectionSound())
		client->GetConnectionSound()->CloseSocket();
	client->GetConnectionTxtAdm()->CloseSocket();
	this->_clientList.remove(client);
	if (this->_mapClientIUser.count(client))
	{
		this->_mtxAdminAcces->Lock();
		AgoraAdmin::ChannelManager::GetChannelManager()->GetChannel(this->_mapClientIUser[client]->GetCurrentChannelId())->RemoveUser(this->_mapClientIUser[client]);
		//AgoraAdmin::UserManager::GetUserManager()->UnloadUser(this->_mapClientIUser[client]->GetId());
		AgoraAdmin::UserManager::GetUserManager()->GetUser(this->_mapClientIUser[client]->GetId())->SetConnected(false);
		AgoraAdmin::AdminInterpreter::ReturnInfo* ret = this->GetAdminInterpreter()->DisconnectUser(AgoraAdmin::UserManager::GetUserManager()->GetUser(this->_mapClientIUser[client]->GetId()));
		this->SendAdmReturnInfo(client, ret);
		this->_mtxAdminAcces->Unlock();
		//delete this->_mapClientIUser[client];
		this->_mapIUserClient.erase(this->_mapClientIUser[client]);
		this->_mapClientIUser.erase(client);
		
	}
	delete client;
	this->_mtxClientListModification->Unlock();
	//ca fait crasher le serveur quand on fait un kick ou ban serveur
	
}

void	Server::AddClient(Client* client)
{
	this->_mtxClientListModification->Lock();
	this->_clientList.push_back(client);
	this->_mtxClientListModification->Unlock();
}

#ifdef _WIN32
ULONGLONG	getTime()
{
	SYSTEMTIME systemTime;
	FILETIME fileTime;
	ULARGE_INTEGER uli;
	ULONGLONG systemTimeIn_ms;
  
	GetSystemTime( &systemTime );
	SystemTimeToFileTime( &systemTime, &fileTime );
	uli.LowPart = fileTime.dwLowDateTime; // could use memcpy here!
	uli.HighPart = fileTime.dwHighDateTime;
	systemTimeIn_ms = uli.QuadPart/10000;
	return systemTimeIn_ms;
}
#else
long	getTime()
{
  struct timeval	tv;

  gettimeofday(&tv, 0);

  return tv.tv_usec / 1000;
}
#endif

IMutex*	Server::GetMutexClientListBrowse()
{
	return (this->_mtxClientListBrowse);
}

IMutex*	Server::GetMutexClientListModification()
{
	return (this->_mtxClientListModification);
}

void	Server::AccesClientList(bool need)
{
	if (need)
		this->_clientListNbAcces++;
	else
		this->_clientListNbAcces--;
}

bool	Server::ClientListIsAccessed()
{
	if (this->_clientListNbAcces)
		return (true);
	return (false);
}

AgoraAdmin::AdminInterpreter*	Server::GetAdminInterpreter()
{
	return this->_interpreter;
}
